#include "Student.h"

Student::Student()
    : name("default")
{
    cout << "Student Contructor called" << endl;
}

Student::~Student()
{
    cout << "Student Destructor called" << endl;
}

string Student::getName()
{
    return name;
}

void Student::setName(string name)
{
    this->name = name;
}

int Student::getAge()
{
    return age;
}

void Student::setAge(int age)
{
    this->age = age;
}

void Student::print()
{
    cout << "My name is: " << name << ", and my age is " << age << endl;
}

///////////////////////////////////////////////

int newStudent(lua_State *L)
{
    Student **s = (Student **)lua_newuserdata(L, sizeof(Student *)); // lua will manage Student** pointer
    *s = new Student;
    luaL_getmetatable(L, "cc.Student");
    lua_setmetatable(L, -2);
    return 1;
}

int l_setName(lua_State *L)
{
    Student **s = (Student **)luaL_checkudata(L, 1, "cc.Student");
    luaL_argcheck(L, s != NULL, 1, "invalid user data");

    luaL_checktype(L, -1, LUA_TSTRING);

    std::string name = lua_tostring(L, -1);
    (*s)->setName(name);
    return 0;
}

int l_setAge(lua_State *L)
{
    Student **s = (Student **)luaL_checkudata(L, 1, "cc.Student");
    luaL_argcheck(L, s != NULL, 1, "invalid user data");
    luaL_checktype(L, -1, LUA_TNUMBER);
    int age = lua_tonumber(L, -1);
    (*s)->setAge(age);
    return 0;
}

int l_getName(lua_State *L)
{
    Student **s = (Student **)luaL_checkudata(L, 1, "cc.Student");
    luaL_argcheck(L, s != NULL, 1, "invalid user data");
    lua_settop(L, 0);
    lua_pushstring(L, (*s)->getName().c_str());
    return 1;
}

int l_getAge(lua_State *L)
{
    Student **s = (Student **)luaL_checkudata(L, 1, "cc.Student");
    luaL_argcheck(L, s != NULL, 1, "invalid user data");
    lua_settop(L, 0);
    lua_pushnumber(L, (*s)->getAge());
    return 1;
}

int l_print(lua_State *L)
{
    Student **s = (Student **)luaL_checkudata(L, 1, "cc.Student");
    luaL_argcheck(L, s != NULL, 1, "invalid user data");
    (*s)->print();

    return 0;
}

int student2string(lua_State *L)
{
    Student **s = (Student **)luaL_checkudata(L, 1, "cc.Student");
    lua_pushfstring(L, "name = %s, age = %d", (*s)->getName().c_str(), (*s)->getAge());

    return 1;
}

static int auto_gc(lua_State *L)
{
    Student **s = (Student **)luaL_checkudata(L, 1, "cc.Student");
    if (s)
    {
        delete *s;
    }

    return 0;
}

static const struct luaL_Reg stuentlib_f[] = {
    {"create", newStudent},
    {NULL, NULL}};
static const struct luaL_Reg studentlib_m[] = {
    {"__tostring", student2string},
    {"setName", l_setName},
    {"setAge", l_setAge},
    {"print", l_print},
    {"getName", l_getName},
    {"getAge", l_getAge},
    {"__gc", auto_gc},
    {NULL, NULL}};

int luaopen_student(lua_State *L)
{
    luaL_newmetatable(L, "cc.Student");
    /* metatable.__index = metatable */
    lua_pushvalue(L, -1); /* duplicates the metatable */
    lua_setfield(L, -2, "__index");

    luaL_setfuncs(L, studentlib_m, 0);

    luaL_newlib(L, stuentlib_f);
    return 1;
}

int lua_test()
{
    lua_State *lua_state;
    lua_state = luaL_newstate();

    static const luaL_Reg lualibs[] =
        {
            {"base", luaopen_base},
            {"io", luaopen_io},
            {"cc", luaopen_student},
            {NULL, NULL}};
    const luaL_Reg *lib = lualibs;
    for (; lib->func != NULL; lib++)
    {
        //注意这里如果使用的不是requiref，则需要手动在Lua里面调用require "模块名"
        luaL_requiref(lua_state, lib->name, lib->func, 1);
        lua_settop(lua_state, 0);
    }
    luaL_openlibs(lua_state);
    luaL_dofile(lua_state, "/home/jinxin/work/Weasel/bin/test.lua");

    lua_close(lua_state);

    return 1;
}